<?php

namespace app\logic\admin;

use ZipArchive;

class L_systemUpdate
{
    public $cmsSystemUrl = ''; //cms系统官网网址 读取最新版本等等操作

    public function __construct()
    {
        $this->cmsSystemUrl="https://cms789admin.ltcms.com";//cms系统官网网址 读取最新版本等等操作
//        $this->cmsSystemUrl=config("app.cmsUrl");//cms系统官网网址 读取最新版本等等操作
    }

    //获取当前md5文件内版本号
    public function getVersion()
    {
        // 获取文件路径
        $filePath = LTCMS_ROOT . "/md5.txt";

        // 打开文件
        $file = fopen($filePath, "r");
        // 读取第一行
        $firstLine = fgets($file);
        // 关闭文件
        fclose($file);
        // 获取 | 符号右侧的内容
        $pipeIndex = strpos($firstLine, "|");
        $rightContent = substr($firstLine, $pipeIndex + 1);
        return $rightContent;

    }

    //检测版本
    public function getNewVersion($paramObj = "")
    {

        $final_arr = logInit();
        //请求获取最新版本数据
        if ($final_arr["error_no"] == 0) {
            $tempsObj = paramsObj();
            $tempsObj->params = array(
                "submitUrl" => $this->cmsSystemUrl . "/api/cmsUpdateLog/getVersionList?env=4",
                "method" => "GET",
                "time" => 0,
            );

            //临时先写死
            $final_arr["error_no"] = 0;
            $final_arr["result"] = file_get_contents($tempsObj->params["submitUrl"]);
        }


        if ($final_arr["error_no"] == 0) {
            $data = json_decode($final_arr["result"], true);
            $final_arr["result"] = $data["data"] ?? [];
            if (!$data || !is_array($data) || !array_key_exists("codeMsg", $data)) {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::OP_FAIL, lang2("获取更新版本信息失败"));
            } else if ($data["codeMsg"] != "success") {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::OP_FAIL, $data["codeMsg"]);
            }
        }

        //写入本地和缓存
        if ($final_arr["error_no"] == 0) {
            $tempSter = array_path($data, "data.0.version", "");
            if ($tempSter) {
                cache()->set("newVersion", $tempSter, 86400);
            }
        }
        return $final_arr;
    }

    //检测md5list 变更
    public function inspectfileMd5ListChange($paramObj = "")
    {
        $dataObj = getParamsObj();
        $final_arr = logInit();
        //校验数据合法
        if ($final_arr["error_no"] == 0) {
            $rules = getKeyDefaultRule(array(           //设置规则 如果为空就用默认规则
                'id' => 'require',
            ));
            $messages = getValidateMessage($rules); //验证规则提示  继承客户端的消息信息
            $dataDefault = array();
            $paramsObj = paramsObj();  //新建一个空对象
            $paramsObj->params = parseToArr($paramObj, "params", array());//  参数
            $paramsObj->mixed = parseToArr($paramObj, "mixed", 1); //获取是否混合数据 1混合 0 只用默认
            $paramsObj->rules = $rules;
            $paramsObj->messages = $messages;
            $paramsObj->dataDefault = $dataDefault;

            $ret = app("verifyParam")::validatorRequest($paramsObj);
            if ($ret["error_no"] == 0) {
                $dataObj->request = $ret["result"];
            } else {
                $final_arr = logCallErrorMsg($final_arr, $ret);
            }
        }
        //下载md5文件列表
        //校验本地数据md5
        if ($final_arr["error_no"] == 0) {
            $updateInfoStr = cache()->get("cmsUpdateLog/info?id=" . $dataObj->request->id);
            if (!$updateInfoStr) { //有缓存直接用
                $updateInfoStr = file_get_contents((new L_systemUpdate)->cmsSystemUrl . "/api/cmsUpdateLog/info?id=" . $dataObj->request->id);
            }

            if ($updateInfoStr) {
                /*                {
                                    "id": 27,
                            "pluginId": 1,
                            "version": 1,
                            "title": "",
                            "type": 4,
                            "contentUrl": "http://www.baidu.com",
                            "time": 1704264355,
                            "updateUrl": "",
                            "updateMd5Url": "",
                            "updateSize": 0,
                            "completeUrl": "http://www.cms.com/static/plugins/xuhaonan89757/update/1/complete_1.zip",
                            "completeMd5Url": "http://www.cms.com/static/plugins/xuhaonan89757/update/1/complete_1.txt",
                            "completeSize": 28744850,
                            "typeFormat": "完整升级包",
                            "timeFormat": "2024-01-03 14:45:55",
                            "updateSizeFormat": "0 B",
                            "completeSizeFormat": "27.41 MB"
                               }*/
                $updateInfo = json_decode($updateInfoStr);

                if ($updateInfo->code == 0) {
                    $updateInfo = $updateInfo->data;
                    cache()->set("cmsUpdateLog/info?id=" . $dataObj->request->id, $updateInfoStr, 86400);
                } else {
                    $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::VERIFY_DATA_FAIL, $updateInfo->codeMsg);
                }

            } else {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::VERIFY_DATA_FAIL, "版本信息获取失败");
            }
        }

        //校验本地数据md5
        if ($final_arr["error_no"] == 0) {
            $md5List = "";
            switch ($updateInfo->type) {
                default:
                    $md5List = file_get_contents($updateInfo->updateMd5Url);
                    break;
                case 4:
                    $md5List = file_get_contents($updateInfo->completeMd5Url);
                    break;
            }

            $result = array(); //[文件名,文件路径,md5是否相同]
            if ($md5List) {
                $md5List = explode("\n", $md5List); // 将内容按行分割到数组中
                foreach ($md5List as $line) {
                    $line = trim($line); // 去除行首尾的空格
                    if (!empty($line)) {
                        $parts = explode('|', $line); // 将每行内容按 "|" 分割为 md5 和路径两部分
                        $md5 = $parts[0];
                        if (strlen($md5) != 32) {
                            continue; //长度不是32 就跳过,
                        }

                        $path = LTCMS_ROOT . $parts[1]; // 将路径前加上 LTCMS_ROOT
                        if (file_exists($path)) { //文件不存在跳过
                            // 计算路径的 md5
                            $calculatedMd5 = md5_file($path);
                            $result[] = array("file" => basename($path), "path" => $path, "isChange" => $calculatedMd5 !== $md5);
                        }
                    }
                }
                $final_arr["result"]["list"] = $result;
                $final_arr["result"]["info"] = get_object_vars($updateInfo);
            } else {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::VERIFY_DATA_FAIL, "更新数据md5List获取失败");
            }
        }

        return $final_arr;
    }

    //检测md5list 变更 备份数据
    public function UpdateBackup($paramObj = "")
    {
        $dataObj = paramsObj();
        $dataObj->request = paramsObj();
        $dataObj->data = paramsObj();
        $dataObj->result = array();
        $final_arr = logInit();


        //校验本地数据md5
        if ($final_arr["error_no"] == 0) {
            $final_arr = $this->inspectfileMd5ListChange();
        }
        //备份不一样的数据
        if ($final_arr["error_no"] == 0) {

            $dir = LTCMS_ROOT . "/runtime/update/";
            if (!is_dir($dir)) {
                @umask(000);
                @mkdir($dir, 0777, true);
            }

            $md5List = $final_arr["result"]["list"];
            $filepath = array();
            foreach ($md5List as $item) {
                if ($item["isChange"]) {
                    $filepath[] = $item["path"];
                }
            }

            //所有待打包文件路径
            $zip = new ZipArchive;
            $zipfilepath = LTCMS_ROOT . "/runtime/update/backups/";
            makeDir($zipfilepath);
            $zipfilepath .= "一键备份" . date("YmdHis") . ".zip";
            $zip->open($zipfilepath, ZipArchive::CREATE);
            foreach ($filepath as $key => $value) {
                $extension = pathinfo($value, PATHINFO_EXTENSION);
                $newString = str_replace(LTCMS_ROOT, "", $value);
                $newString = str_replace("\\", "/", $newString);
                if ($extension == "zip") {
                    continue;
                }
                $zip->addFile($value, $newString); //添加到压缩包
            }
            $zip->close();
            //自定义处理

            $final_arr["error_msg"] = "被修改文件已备份到" . $zipfilepath;

        }


        return $final_arr;
    }

    //更新版本
    public function updateNewVersion($paramObj = "")
    {
        $dataObj = getParamsObj();
        $final_arr = logInit();
        //校验数据合法
        if ($final_arr["error_no"] == 0) {
            $rules = getKeyDefaultRule(array(           //设置规则 如果为空就用默认规则
                'id' => 'require',
            ));
            $messages = getValidateMessage($rules); //验证规则提示  继承客户端的消息信息
            $dataDefault = array();
            $paramsObj = paramsObj();  //新建一个空对象
            $paramsObj->params = parseToArr($paramObj, "params", array());//  参数
            $paramsObj->mixed = parseToArr($paramObj, "mixed", 1); //获取是否混合数据 1混合 0 只用默认
            $paramsObj->rules = $rules;
            $paramsObj->messages = $messages;
            $paramsObj->dataDefault = $dataDefault;

            $ret = app("verifyParam")::validatorRequest($paramsObj);
            if ($ret["error_no"] == 0) {
                $dataObj->request = $ret["result"];
            } else {
                $final_arr = logCallErrorMsg($final_arr, $ret);
            }
        }

        //校验获取版本数据
        if ($final_arr["error_no"] == 0) {
            $updateInfoStr = cache()->get("cmsUpdateLog/info?id=" . $dataObj->request->id);
            if (!$updateInfoStr) { //有缓存直接用
                $updateInfoStr = file_get_contents((new L_systemUpdate)->cmsSystemUrl . "/api/cmsUpdateLog/info?id=" . $dataObj->request->id);
            }

            if ($updateInfoStr) {
                /*                {
                                    "id": 27,
                            "pluginId": 1,
                            "version": 1,
                            "title": "",
                            "type": 4,
                            "contentUrl": "http://www.baidu.com",
                            "time": 1704264355,
                            "updateUrl": "",
                            "updateMd5Url": "",
                            "updateSize": 0,
                            "completeUrl": "http://www.cms.com/static/plugins/xuhaonan89757/update/1/complete_1.zip",
                            "completeMd5Url": "http://www.cms.com/static/plugins/xuhaonan89757/update/1/complete_1.txt",
                            "completeSize": 28744850,
                            "typeFormat": "完整升级包",
                            "timeFormat": "2024-01-03 14:45:55",
                            "updateSizeFormat": "0 B",
                            "completeSizeFormat": "27.41 MB"
                               }*/
                $updateInfo = json_decode($updateInfoStr);

                if ($updateInfo->code == 0) {
                    $updateInfo = $updateInfo->data;
                    cache()->set("cmsUpdateLog/info?id=" . $dataObj->request->id, $updateInfoStr, 86400); //写入或续期
                } else {
                    $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::VERIFY_DATA_FAIL, $updateInfo->codeMsg);
                }

            } else {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::VERIFY_DATA_FAIL, "版本信息获取失败");
            }
        }

        //请求获取最新版本数据
        if ($final_arr["error_no"] == 0) {
            $downloadUrl = "";
            $downloadSize = 0;

            switch ($updateInfo->type) {
                default:
                    $downloadUrl = $updateInfo->updateUrl;
                    $downloadSize = $updateInfo->updateSize;
                    break;
                case 4:
                    $downloadUrl = $updateInfo->completeUrl;
                    $downloadSize = $updateInfo->completeSize;
                    break;
            }
            $downloadPath = LTCMS_ROOT . "/runtime/update/zip/" . $updateInfo->pluginId;
            makeDir($downloadPath);
            $downloadPath .= "/" . basename($downloadUrl);
            #下载文件
            $test = file_get_contents($downloadUrl);


            $red = file_put_contents($downloadPath . ".temp", $test);

            /*          set_time_limit(3000); //更新文件50分钟足够了
                        $ch = curl_init();
                        curl_setopt($ch, CURLOPT_URL, $url);
                        $fp = fopen($dataObj->data["file"], 'w+');
                        curl_setopt($ch, CURLOPT_FILE, $fp);
                        $red = curl_exec($ch);
                        curl_close($ch);
                        fclose($fp);
                        //判断下载成功还是失败
            */
            if (!$red || strlen($test) < $downloadSize / 2) {  //防止误差,如果比正常包一半还小,大概率下载失败了
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::OP_FAIL, lang2("更新文件下载失败"));
            }
        }

        //下载成功,开始解压并覆盖原目录
        if ($final_arr["error_no"] == 0) {
            $zipFile = $downloadPath . ".temp"; // 压缩文件路径
            $extractTo = LTCMS_ROOT; // 解压目标目录
            $zip = new ZipArchive;
            if ($zip->open($zipFile) === TRUE) {
                // 解压文件到指定目录
                if (env()->get("APP_debug", "") == 1) {
                    sleep(2); //调试模式,暂时不覆盖了
                } else {
                    $zip->extractTo($extractTo);
                }
                $zip->close();
                $final_arr["error_msg"] = "更新成功,请刷新网页";
            } else {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::OP_FAIL, lang2("解压缩失败,请重试"));
            }
        }
        //更新成功,删除所有缓存信息,重新初始化缓存.防止缓存残留,导致新功能无法生效,比如插件
        //20240117 因为可能有主动操作才生成的缓存,所以不能全删,后续改到升级后兼容文件内去删除缓存
       /* if ($final_arr["error_no"] == 0) {
            $cacheFile = LTCMS_ROOT . "/runtime/cache";
            if (!rmdir($cacheFile)) { //如果删除目录失败了,就单独删每个文件
                //枚举所有文件,单独删除,防止某个文件占用导致所有文件都删除失败,循环删,失败的只有单独占用的.
                // 获取目录下的所有文件
                $files = getPathAllFiles($cacheFile);
                // 逐个删除文件
                foreach ($files as $file) {
                    if (is_file($file)) {
                        @unlink($file);
                    }
                }
            }
        }*/
        //更新成功,修改新版本压缩包文件名
        if ($final_arr["error_no"] == 0) {
            //先删旧当前版本压缩包,修改新版本压缩包为当前版本压缩包,备用
            //直接移动
            $sourceFile = $downloadPath . ".temp"; // 源文件路径
            if (!rename($sourceFile, $downloadPath)) {
                //失败,忽略
            }
        }
        //数据库版本及兼容
        if ($final_arr["error_no"] == 0) {
            if ($updateInfo->pluginId == 1) { //系统的更新单独处理
                $name = "init_" . str_replace(".", "_", $updateInfo->version); //更新文件的文件名
                $path = LTCMS_ROOT . "/app/updateInit/" . $name;
                if (is_file($path . ".php")) { //文件存在则执行,不存在就跳过
                    include_once $path . ".php";
                    $classlist = get_declared_classes();
                    $className = str_replace("/", "\\", "app/updateInit/" . $name);
                    $obj = new $className();
                    $checkResult = $obj->init(LT_VERSION, $updateInfo);
                    if ($checkResult["error_no"] != 0) {
                        $final_arr = logCallErrorMsg($final_arr, $checkResult);
                    }
                }
            }
        }
        return $final_arr;
    }

}